#include <video.h>
#include <kernel/definiciones.h>
#include <kernel/system.h>
#include <kernel/sched.h>
#include <kernel/pcb.h>
#include <kernel/gdt.h>

extern unsigned long ulProcActual;
/**
/brief Planificador Round - Robin
/date 16/10/2008
*/
void vFnPlanificadorRR(){
	
  unsigned int uiVectorDescriptorAuxiliarTarea[2];
  int iNUltimaTarea = -1;	/*,iFlags; */

    unsigned short int bInterrupcionesHabilitadas = 0;
    int iFlags;

    /* 16/10/2008 - Se agrega la deshablitacion de interrupciones
     *
     * Si no se deshabiltian las interrupciones, puede darse el caso en que
     * mientras se ejecuta el planificador, se presente una interrupcion que
     * tambien llame al planificador. Ambos planificadores llamaran intentaran
     * poner en ejecucion a la misma tarea; cuando el segundo de ellos lo haga, 
     * causara una Excepcion de Proteccion General, por querer ejecutar una
     * tarea marcada como ocupada.
     * Al deshabilitar las interrupciones se evitan estos problemas.
     */

    //Deshabilitacion de interrupciones
    __asm__ ("pushf\n pop %%eax": "=a" (iFlags):);
    // si estaban habilitadas, aqui se deshabilitan
    if (iFlags & 0x200){
        __asm__ ("cli"::);
        bInterrupcionesHabilitadas = 1;
    }

	//vFnImprimir("\nUltima tarea %d",staiN);
      iNUltimaTarea = staiN;
      staiN++;
      while (staiN < CANTMAXPROCS && pstuPCB[staiN].iEstado != PROC_LISTO)
	{
	  staiN++;
	}
    // Si llegamos al final del vector volvemos al primer elemento (distinto de
    // la tarea nula)
      if (staiN == CANTMAXPROCS)	
	{			
	  staiN = 1;
	  while (staiN < iNUltimaTarea
		 && pstuPCB[staiN].iEstado != PROC_LISTO)
	    {
	      staiN++;
	    }
	  if (staiN == iNUltimaTarea &&
	      (pstuPCB[staiN].iEstado == PROC_ELIMINADO
	       || pstuPCB[staiN].iEstado == PROC_NO_DEFINIDO))
	    //definitivamente ningun proceso
	    {			//listo para la ejecucion
	      staiN = 0;	//ejecuto la tarea nula
	    }
	}

	//vFnImprimir("\nProx tarea %d",staiN);
      if (pstuPCB[staiN].iEstado == PROC_LISTO)
	{
	  if (staiProcesoAnterior != staiN)
	    {
	      vFnImprimirContextSwitch (ROJO, pstuPCB[staiN].ulId,
					pstuPCB[staiN].stNombre,
					pstuPCB[staiN].uiIndiceGDT_TSS);
	      if (staiProcesoAnterior != -1 
		  && pstuPCB[staiProcesoAnterior].iEstado != PROC_ELIMINADO
		  && pstuPCB[staiProcesoAnterior].iEstado != PROC_ZOMBIE
		  && pstuPCB[staiProcesoAnterior].iEstado != PROC_ESPERANDO
		  && pstuPCB[staiProcesoAnterior].iEstado != PROC_DETENIDO)
		  	pstuPCB[staiProcesoAnterior].iEstado = PROC_LISTO;	//paso el proceso que estaba en ejecución a listo        
			
		  pstuPCB[staiN].iEstado = PROC_EJECUTANDO;	//paso al actual de listo a running
	      staiProcesoAnterior = staiN;

	      ulProcActual = staiN;

            /* uiVectorDescriptorAuxiliarTarea[0] = offset.
             * Este parametro no hace falta cargarlo porque es ingnorado al
             * momento del salto a un descriptor, lo unico que interesa el el
             * selector en si mismo */

            /* multiplicamos el indice por 8 para tener el offset en bytes desde
             * el inicio de la gdt hasta el selector que nos interesa */
            uiVectorDescriptorAuxiliarTarea[1] = pstuPCB[staiN].uiIndiceGDT_TSS * 8;

			//vFnImprimir("\nProx tarea stuTSSTablaTareas %d",stuTSSTablaTareas[pstuPCB[staiN].uiIndiceGDT_TSS * 8].cr3);
			BX_MAGIC_BREAKPOINT;
            asm("clts\t\n" "ljmp *%0": :"m"(*uiVectorDescriptorAuxiliarTarea));

			//vFnImprimir("\nProx tarea q se carga %d",staiN);
	    }
	}

    //Habilitacion de interrupciones
    //Si estaban habilitadas, se vuelven a habilitar
    if (bInterrupcionesHabilitadas)
      __asm__ ("sti"::);
}
